package limiter

import "time"

type GBRateLimiter struct {
	consumed int
	expireAt int64
}

// 分配新对象
func GBNewRateLimiter() *GBRateLimiter {
	return &GBRateLimiter{
		consumed: 0,
		expireAt: 0,
	}
}

// 是否过期
func (obj *GBRateLimiter) Expired() bool {
	return time.Now().UnixNano() > obj.expireAt - 1
}

// 重置对象
func (obj *GBRateLimiter) Reset(intervals int) {
	obj.consumed = 0
	obj.expireAt = time.Now().UnixNano() + int64(intervals) * int64(time.Second)
}

// 取得剩余额度
//
// @param intervals 周期间隔（秒数）
// @param limit 每周期最大额度
//
// @return 剩余额度数量，可能是负数
//
func (obj *GBRateLimiter) GetLeft(intervals, limit int) int {
	// 参数错误
	if intervals < 1 || limit == 0 {
		return 0
	}
	// 已经过期，重置
	if obj.Expired() {
		obj.Reset(intervals)
	}
	// 返回剩余数量
	return limit - obj.consumed
}

// 速率限制检测，是否被限制
//
// @param intervals 周期间隔（秒数）
// @param limit 每周期最大额度
//
// @return true：被限制，false：未被限制
//
func (obj *GBRateLimiter) IsLimited(intervals, limit int) bool {
	// 参数错误
	if intervals < 1 || limit == 0 {
		return true
	}
	// 已经过期，重置
	if obj.Expired() {
		obj.Reset(intervals)
	}
	// 消费满了则限制
	return obj.consumed > limit - 1
}

// 速率限制检测消费
//
// @param intervals 周期间隔（秒数）
// @param limit 每周期最大额度
// @param consume 本次消费额度，可以传"0"表示不消费
//
// @return true：被限制，false：未被限制，当实际消费额度大于"0"时，一定会返回"false"
// @return 本次实际消费额度
// @return 本周期剩余额度
//
func (obj *GBRateLimiter) CheckLimit(intervals, limit, consume int) (bool, int, int) {
	// 参数错误
	if intervals < 1 || limit == 0 {
		return true, 0, 0
	}
	// 已经过期，重置
	if obj.Expired() {
		obj.Reset(intervals)
	}
	// 需要更新消费，本次消费不能超过最大限制
	if consume > limit {
		consume = limit
	}
	// 判断是否已经超限
	if obj.consumed > limit - 1 {
		return true, 0, 0
	}
	// 未达到限制，计算剩余额度
	leftover := limit - obj.consumed
	// 本次消费不能超过剩余数，计算出实际消费
	if consume > leftover {
		consume = leftover
	}
	// 未达到限制，增加限流数据
	obj.consumed += consume
	// 未被限制，实际消费，剩余
	return false, consume, leftover - consume
}

// 速率限制检测消费，是否被限制
//
// @param intervals 周期间隔（秒数）
// @param limit 每周期最大额度
// @param consume 本次消费额度，可以传"0"表示不消费
//
// @return true：被限制，false：未被限制，当实际消费额度大于"0"时，一定会返回"false"
//
func (obj *GBRateLimiter) CheckIsLimited(intervals, limit, consume int) bool {
	limited, _, _ := obj.CheckLimit(intervals, limit, consume)
	return limited
}